<template>
    <div class="sc-upload-file">
        <el-upload :disabled="disabled" :auto-upload="autoUpload" :action="action" :name="name" :data="data"
            :http-request="request" v-model:file-list="defaultFileList" :show-file-list="showFileList" :drag="drag"
            :accept="accept" :multiple="multiple" :limit="limit" :before-upload="before" :on-success="success"
            :on-error="error" :on-preview="handlePreview" :on-exceed="handleExceed">
            <slot>
                <el-button type="primary" :disabled="disabled">{{ $t('upload.selectFile') }}</el-button>
                <div class="el-upload__text">{{ $t('upload.selectFileTips') }}</div>
            </slot>
            <template #tip>
                <div v-if="tip" class="el-upload__tip">{{ tip }}</div>
            </template>
        </el-upload>
        <span style="display:none!important"><el-input v-model="files"></el-input></span>
    </div>
</template>
<script setup>
import { ref, watch, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import config from '@/config/upload';
import i18n from '@/locales';

// 定义组件的 props
const props = defineProps({
    modelValue: { type: [String, Array], default: "" }, // 绑定的值，可以是字符串或数组
    tip: { type: String, default: "" }, // 上传区域的提示文本
    action: { type: String, default: "" }, // 上传的服务器地址
    apiObj: { type: Object, default: () => { } }, // 用于自定义上传行为的对象
    name: { type: String, default: config.filename }, // 上传时文件的字段名
    data: { type: Object, default: () => { } }, // 上传时附加的额外参数
    accept: { type: String, default: "" }, // 接受上传的文件类型
    maxSize: { type: Number, default: config.maxSizeFile }, // 文件最大尺寸限制
    limit: { type: Number, default: 0 }, // 最大允许的上传文件数量
    autoUpload: { type: Boolean, default: true }, // 是否自动上传
    showFileList: { type: Boolean, default: true }, // 是否显示文件列表
    drag: { type: Boolean, default: false }, // 是否启用拖拽上传
    multiple: { type: Boolean, default: true }, // 是否支持多文件上传
    disabled: { type: Boolean, default: false }, // 是否禁用上传
    onSuccess: { type: Function, default: () => { return true } } // 上传成功的回调
});
// 定义组件可以发出的事件
const emit = defineEmits(['update:modelValue']);
// 组件内部使用的响应式引用
const files = ref(''); // 存储文件列表的字符串表示
const defaultFileList = ref([]); // 默认的文件列表
// 监听 modelValue 的变化
watch(() => props.modelValue, (val) => {
    // 根据 val 的类型（数组或字符串）进行处理
    // 并同步更新 defaultFileList 和 files
    // 此部分逻辑用于将外部传入的 modelValue 转换为组件内部使用的数据格式
    if (Array.isArray(val)) {
        if (JSON.stringify(val) != JSON.stringify(formatArr(defaultFileList.value))) {
            defaultFileList.value = val;
            files.value = val;
        }
    } else {
        if (val != toStr(defaultFileList.value)) {
            defaultFileList.value = toArr(val);
            files.value = val;
        }
    }
});
// 监听 defaultFileList 的变化
watch(defaultFileList, (val) => {
    // 当 defaultFileList 变化时，发出 update:modelValue 事件
    // 并更新 files 的值
    emit('update:modelValue', Array.isArray(props.modelValue) ? formatArr(val) : toStr(val));
    files.value = toStr(val);
}, { deep: true });
// 组件挂载时的操作
onMounted(() => {
    // 初始化 defaultFileList 和 files 的值
    defaultFileList.value = Array.isArray(props.modelValue) ? props.modelValue : toArr(props.modelValue);
    files.value = props.modelValue;
});
// 将字符串转换为文件数组
const toArr = (str) => {
    var _arr = []
    var arr = str.split(",")
    arr.forEach(item => {
        if (item) {
            var urlArr = item.split('/');
            var fileName = urlArr[urlArr.length - 1]
            _arr.push({
                name: fileName,
                url: item
            })
        }
    })
    return _arr
};
// 将文件数组转换为字符串
const toStr = (arr) => arr.map(v => v.url).join(",");
// 格式化文件数组
const formatArr = (arr) => {
    var _arr = []
    arr.forEach(item => {
        if (item) {
            _arr.push({
                name: item.name,
                url: item.url
            })
        }
    })
    return _arr
};
// 上传前的钩子函数
const before = (file) => {
    const maxSize = file.size / 1024 / 1024 < props.maxSize;
    if (!maxSize) {
        ElMessage.warning(i18n.global.t('upload.warningUploadBefore', { maxSize: props.maxSize }));
        return false;
    }
};
// 上传成功的钩子函数
const success = (res, file) => {
    var os = props.onSuccess(res, file);
    if (os != undefined && os == false) {
        return false;
    }
    var response = config.parseData(res);
    file.name = response.name;
    file.url = response.src;
};
// 上传失败的钩子函数
const error = (err) => {
    ElMessage.error(err);
};
// 移除文件前的钩子函数
const beforeRemove = (uploadFile) => {
    return ElMessageBox.confirm(i18n.global.t('upload.confirmRemoveUploadFile', { uploadFile: uploadFile.name }), i18n.global.t('common.tip'), {
        type: 'warning'
    }).then(() => {
        return true
    }).catch(() => {
        return false
    })
};
// 超过文件数量限制的钩子函数
const handleExceed = () => {
    ElMessage.warning(i18n.global.t('upload.warningUploadLimit', { limit: props.limit }));
};
// 预览文件的钩子函数
const handlePreview = (uploadFile) => {
    window.open(uploadFile.url);
};
// 自定义上传请求的处理函数
const request = (param) => {
    var apiObj = config.apiObj;
    if (props.apiObj) {
        apiObj = props.apiObj;
    }
    const data = new FormData();
    data.append(param.filename, param.file);
    for (const key in param.data) {
        data.append(key, param.data[key]);
    }
    apiObj.post(data, {
        onUploadProgress: e => {
            const complete = parseInt(((e.loaded / e.total) * 100) | 0, 10)
            param.onProgress({ percent: complete })
        }
    }).then(res => {
        var response = config.parseData(res);
        if (response.code == config.successCode) {
            param.onSuccess(res)
        } else {
            param.onError(response.msg || i18n.global.t('error.unknwon'))
        }
    }).catch(err => {
        param.onError(err)
    })
};
</script>
<style scoped>
.el-form-item.is-error .sc-upload-file:deep(.el-upload-dragger) {
    border-color: var(--el-color-danger);
}

.sc-upload-file {
    width: 100%;
}

.sc-upload-file:deep(.el-upload-list__item) {
    transition: none !important;
}

.sc-upload-file:deep(.el-upload__text) {
    margin-top: 10px;
}</style>